#pragma once
#include<algorithm>
#include<functional>

namespace xhy
{
	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		unique_ptr(unique_ptr&& up)
		{
			std::swap(_ptr, up._ptr);
		}

		unique_ptr& operator=(unique_ptr&& up)
		{
			std::swap(_ptr, up._ptr);
			return* this;
		}

		unique_ptr(unique_ptr& up) = delete;
		unique_ptr& operator=(unique_ptr& up) = delete;

		~unique_ptr()
		{
			delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator[](size_t i)
		{
			return _ptr[i];
		}

		T* get()
		{
			return _ptr;
		}

	private:
		T* _ptr;
	};

	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{
		}

		auto_ptr(auto_ptr& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		auto_ptr(auto_ptr&& ap)
		{
			std::swap(_ptr, ap._ptr);
		}

		auto_ptr& operator=(auto_ptr& ap)
		{
			if (this != &ap)
			{
				if (_ptr != nullptr)
				{
					delete _ptr;
					_ptr = nullptr;
				}

				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}

			return *this;
		}

		auto_ptr& operator=(auto_ptr&& ap)
		{
			std::swap(_ptr, ap._ptr);
			return* this;
		}

		~auto_ptr()
		{
			delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator[](size_t i)
		{
			return _ptr[i];
		}

		T* get()
		{
			return _ptr;
		}

	private:
		T* _ptr;
	};

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_count(new int(1))
		{ }

		template<class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			, _count(new int(1))
			, _del(del)
		{}

		void swap(shared_ptr& sp)
		{
			std::swap(_ptr, sp._ptr);
			std::swap(_count, sp._count);
		}

		shared_ptr(shared_ptr&& sp)
		{
			swap(sp);
		}

		shared_ptr& operator=(shared_ptr&& sp)
		{
			if (_ptr == sp._ptr)
			{
				swap(sp);
			}

			return *this;
		}

		shared_ptr(const shared_ptr& sp)
			:_ptr(sp._ptr)
			, _count(sp._count)
		{
			++(*_count);
		}

		void reset()
		{
			if (--(*_count) == 0)
			{
				_del(_ptr);
				delete _count;
				_ptr = nullptr;
				_count = nullptr;
			}
		}

		shared_ptr& operator=(shared_ptr& sp)
		{
			reset();

			if (_ptr != sp._ptr)
			{
				_ptr = sp._ptr;
				_count = sp._count;
				++(*_count);
			}

			return *this;
		}

		~shared_ptr() 
		{ 
			reset(); 
		}

		T& operator*() { return *_ptr; }
		T* operator->() { return _ptr; }
		T& operator[](size_t i) { return _ptr[i]; }

	private:
		T* _ptr = nullptr;
		int* _count = nullptr;
		function<void(T*)> _del = [=](T* ptr) {delete ptr; };
	};
}